package cn.uidcc.fast.jackson.util;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class DateUtils {

    public static final TimeZone DEFAULT_TIMEZONE = TimeZone.getTimeZone("GMT+8");

    public static final Locale DEFAULT_LOCALE = Locale.SIMPLIFIED_CHINESE;

    public static final Calendar CALENDAR = new GregorianCalendar(DEFAULT_TIMEZONE, DEFAULT_LOCALE);

    public static final String PATTERN_ISO8601 = "\\d{4}[-]\\d{2}[-]\\d{2}[T]\\d{2}[:]\\d{2}(?:[:]\\d{2})?(\\.\\d+)?(Z|[+-]\\d{2}(?:[:]?\\d{2})?)?";

    // constant datetime
    public static final String DATE_STR = "yyyyMMdd";
    public static final String DATE_KEY_STR = "yyMMddHHmmss";
    public static final String DATE_KEY_STR_FULL = "yyyyMMddHHmmss";
    public static final String DATE_MILLISECOND_STR = "yyMMddHHmmssSSS";
    public static final String DATE_MILLISECOND_STR_FULL = "yyyyMMddHHmmssSSS";
    public static final String DATE_SMALL_STR = "yyyy-MM-dd";
    public static final String DATE_FULL_STR = "yyyy-MM-dd HH:mm:ss";
    public static final String DATE_ISO8601_STR = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";



    /**
     * trans str to date on pattern
     * @param dateStr
     * @param pattern
     * @return
     */
    public static Date parse(String dateStr, String pattern) throws ParseException {
        if (DATE_ISO8601_STR.equals(pattern)) {
            Calendar cal = (Calendar) CALENDAR.clone();
            cal.setLenient(true);
            cal.clear();
            Matcher m = Pattern.compile(PATTERN_ISO8601).matcher(dateStr);
            int totalLen = dateStr.length();
            if (m.matches()) {
                int start = m.start(2);
                int end = m.end(2);
                int len = end - start;
                int offsetSecs;
                if (len > 1) {
                    offsetSecs = _parse2D(dateStr, start + 1) * 3600;
                    if (len >= 5) {
                        offsetSecs += _parse2D(dateStr, end - 2) * 60;
                    }

                    if (dateStr.charAt(start) == '-') {
                        offsetSecs *= -1000;
                    } else {
                        offsetSecs *= 1000;
                    }

                    cal.set(Calendar.ZONE_OFFSET, offsetSecs);
                    cal.set(Calendar.DST_OFFSET, 0);
                }

                int year = _parse4D(dateStr, 0);
                int month = _parse2D(dateStr, 5) - 1;
                int day = _parse2D(dateStr, 8);
                int hour = _parse2D(dateStr, 11);
                int minute = _parse2D(dateStr, 14);
                int seconds;
                if (totalLen > 16 && dateStr.charAt(16) == ':') {
                    seconds = _parse2D(dateStr, 17);
                } else {
                    seconds = 0;
                }

                cal.set(year, month, day, hour, minute, seconds);
                start = m.start(1) + 1;
                end = m.end(1);
                int msecs = 0;
                if (start >= end) {
                    cal.set(Calendar.MILLISECOND, 0);
                } else {
                    msecs = 0;
                    int fractLen = end - start;
                    switch(fractLen) {
                        default:
                            if (fractLen > 9) {
                                throw new ParseException(String.format("Cannot parse date \"%s\": invalid fractional seconds '%s'; can use at most 9 digits", dateStr, m.group(1).substring(1)), start);
                            }
                        case 3:
                            msecs += dateStr.charAt(start + 2) - 48;
                        case 2:
                            msecs += 10 * (dateStr.charAt(start + 1) - 48);
                        case 1:
                            msecs += 100 * (dateStr.charAt(start) - 48);
                        case 0:
                            cal.set(Calendar.MILLISECOND, msecs);
                    }
                }

                return cal.getTime();
            }
            return null;
        } else {
            SimpleDateFormat df = new SimpleDateFormat(pattern);
            return df.parse(dateStr);
        }

    }

    private static int _parse2D(String str, int index) {
        return 10 * (str.charAt(index) - 48) + (str.charAt(index + 1) - 48);
    }

    private static int _parse4D(String str, int index) {
        return 1000 * (str.charAt(index) - 48) + 100 * (str.charAt(index + 1) - 48) + 10 * (str.charAt(index + 2) - 48) + (str.charAt(index + 3) - 48);
    }
}
